package com.sinog.biz.service.api.impl;

import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.ZipUtil;
import com.sinog.biz.service.api.XmlMinitoringService;
import com.sinog.biz.service.api.async.AsyncUploadFileToFtp;
import com.sinog.biz.service.business.VerificationService;
import com.sinog.biz.service.business.impl.UploadAppendixFileFeignServiceImpl;
import com.sinog.biz.service.xml.InterfaceDockingService;
import com.sinog.biz.service.xml.InterfaceXjService;
import com.sinog.biz.util.FileUtil;
import com.sinog.biz.util.FtpUtil;
import com.sinog.core.model.UploadAppendixFile;
import com.sinog.core.utilbean.Constants;
import com.sinog.core.utilbean.IdGen;
import com.sinog.core.utilbean.Result;
import com.sinog.core.utilbean.ReturnCode;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.net.ftp.FTPClient;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.dom4j.io.SAXReader;
import org.dom4j.io.XMLWriter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.xml.sax.SAXException;
import javax.annotation.Resource;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;

/**
 * @desc 前置机上传zip包监听接口service实现类
 * @author - lzq
 * @date -
 */
@Slf4j
@Service
public class XmlMonitoringServiceImpl implements XmlMinitoringService {

    /**
     * 前置机ftp ip
     */
    @Value("${gkzx.azbj.zip.pull.ip}")
    private String ip;
    /**
     * 前置机ftp port
     */
    @Value("${gkzx.azbj.zip.pull.port}")
    private int port;
    /**
     * 前置机ftp username
     */
    @Value("${gkzx.azbj.zip.pull.username}")
    private String username;
    /**
     * 前置机ftp password
     */
    @Value("${gkzx.azbj.zip.pull.password}")
    private String password;
    /**
     * 前置机ftp timeout
     */
    @Value("${gkzx.azbj.zip.pull.timeout}")
    private int tomeout;
    /**
     * 前置机ftp 下载zip包路径
     */
    @Value("${gkzx.azbj.zip.pull.ftppath}")
    private String ftppath;
    /**
     * 发送前置机ftp ip
     */
    @Value("${gkzx.azbj.zip.push.ip}")
    private String pushFtpIp;
    /**
     * 发送前置机ftp port
     */
    @Value("${gkzx.azbj.zip.push.port}")
    private int pushFtpPort;
    /**
     * 发送前置机ftp username
     */
    @Value("${gkzx.azbj.zip.push.username}")
    private String pushFtpUsername;
    /**
     * 发送前置机ftp password
     */
    @Value("${gkzx.azbj.zip.push.password}")
    private String pushFtpPassword;
    /**
     * 发送文件上传ftp目录
     */
    @Value("${gkzx.azbj.zip.push.ftpUploadPath}")
    private String ftpUploadPath;
    @Resource
    private InterfaceDockingService interfaceDockingService;
    @Resource
    private InterfaceXjService interfaceXjService;
    @Resource
    private AsyncUploadFileToFtp asyncUploadFileToFtp;
    @Resource
    private UploadAppendixFileFeignServiceImpl uploadAppendixFileFeignService;
    @Resource
    private VerificationService verificationService;

    /**
     * 描述
     * @param zipName zip包名称
     */
    @Override
    public Result monitoring(String zipName) {
        //zip包名字，去掉后缀
        String localpathThree = zipName.split("\\.")[0] + "/";
        //xml文件名，包含后缀

         String xmlFileName = zipName.split("_")[0] + "_" + zipName.split("_")[1] + "_" + zipName.split("_")[2] + "_" + zipName.split("_")[3]
            + "_" + zipName.split("_")[4] + "_" + zipName.split("_")[5] + "_" + zipName.split("_")[6] + Constants.ImageEndType.FILE_END_XML;
        //从ftp服务器下载zip包到本地服务器并解压
        boolean b = downFileFromFtp(zipName);
        if(b){
            //流程编号
            String processType = zipName.split("_")[1];
            //解析
            return analysisXml(localpathThree,xmlFileName,processType,zipName);
            //todo 返回之前需要组装两个回执xml文件到前置机ftp服务器
        }
        return Result.failure(ReturnCode.FAILURE);
    }

    /**
     * 从ftp下载zip包到本地
     * @param zipName 压缩包文件名+后缀
     * @return boolean
     */
    private boolean downFileFromFtp(String zipName){
        //============================================1、从远程ftp下载文件到本地服务器 ===============================================
        //从ftp服务器下载
        FTPClient ftp = FtpUtil.getFtpClient(ip,port,username,password);
        //设置ftp连接超时时间5秒
        ftp.setConnectTimeout(tomeout);
        String localpath = FileUtil.getFilePathDetails().get("localpath");
        String localpathTwo = FileUtil.getFilePathDetails().get("localpathTwo");
        log.info("开始从前置机{}，下载zip包：{},到本地服务器{}目录",ip + ":" + ftppath,zipName,localpath);
        FtpUtil.downloadFile(ftp,ftppath,zipName,localpath);
        //============================================2、解压本地服务器指定路径下的zip包到指定目录 ===============================================
        //1、解压本地zip包到指定目录
        String zipPath = localpath + zipName;
        String jyPath = localpath + localpathTwo;
        File unzip = ZipUtil.unzip(zipPath, jyPath+zipName.split("\\.")[0]);
        boolean exists = unzip.exists();
        if(exists){
            log.info("解压成功，zip包{}，成功解压到{}",zipPath,jyPath);
            return true;
        }
        return false;
    }

    /**
     * 解析xml
     * @param localpathThree zip包名字，去掉后缀
     * @param xmlFileName xml文件名+后缀名
     * @param processType 流程编号
     * @param zipName 压缩包名称 + 后缀
     * @return Result
     */
    private Result analysisXml(String localpathThree,String xmlFileName,String processType,String zipName){
        try {
            //1.到解压后的目录把xml解析成对象、结构化数据存储到对应的目录
            SAXReader saxReader = new SAXReader();
            saxReader.setFeature("http://apache.org/xml/features/disallow-doctype-decl",true);
            String localpath = FileUtil.getFilePathDetails().get("localpath");
            String localpathTwo = FileUtil.getFilePathDetails().get("localpathTwo");
            //2.读取xml文件
            Document read;
            try {
                //如果zip包有一层和zip名相同的目录，则读取目录下的xml，找不到目录则catch里面向上一层找xml
                read = saxReader.read(new File(localpath + localpathTwo + localpathThree + xmlFileName));
            } catch(Exception e) {
                log.error(e.getMessage());
                read = saxReader.read(new File(localpath + localpathTwo + xmlFileName));
            }
            //3.通过Document对象获取根元素
            Element rootElement = read.getRootElement();
            //4.定义根节点
            String rootElementName;
            //5.定义根节点子节点集合
            Map<String,String> rootElementsChilds = new HashMap<>(16);
            Element element = null;
            Element elementMessage = null;
            Element elementAjxx = null;
            switch(processType) {
                case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_A:
                    //预安置人员信息录入
                    rootElementName = "XYR";
                    rootElementsChilds.put("JBXX","0");
                    rootElementsChilds.put("FXXX","0");
                    rootElementsChilds.put("SHGX","1");
                    rootElementsChilds.put("ZNXX","1");
                    element = rootElement.element(rootElementName);
                    elementAjxx = rootElement.element("AJXX");
                    elementMessage = rootElement.element("MESSAGE");
                    break;
                case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_C:
                    //出监评估信息
                    rootElementName = "YWXX";
                    rootElementsChilds.put("JXJS","1");
                    rootElementsChilds.put("CJPG","0");
                    element = rootElement.element(rootElementName);
                    elementMessage = rootElement.element("MESSAGE");
                    break;
                case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_D:
                    //发起衔接操作
                    rootElementName = "AJXX";
                    rootElementsChilds.put("JBXX","0");
                    element = rootElement.element(rootElementName);
                    elementMessage = rootElement.element("MESSAGE");
                    break;
                default:
                    break;
            }
            return analysisXml(element,rootElementsChilds,processType,elementAjxx,elementMessage, zipName);
        } catch(DocumentException | SAXException e) {
            e.printStackTrace();
        }
        return Result.failure(ReturnCode.FAILURE);
    }

    /**
     * 解析流程
     * @param rootElement rootElement
     * @param rootElementsChilds rootElementsChilds
     * @param processType processType
     * @param elementAjxx elementAjxx
     * @param elementMessage elementMessage
     * @param zipName 压缩包名称 + 后缀
     * @return Result
     */
    private Result analysisXml(Element rootElement,Map<String,String> rootElementsChilds,String processType,Element elementAjxx,Element elementMessage,String zipName) throws SAXException, DocumentException {
        Map<String, List<Map<String, String>>> datas = new HashMap<>(16);
        for(Map.Entry<String, String> entry : rootElementsChilds.entrySet()) {
            String key = entry.getKey();
            String value = entry.getValue();
            List<Element> elements = rootElement.elements(key);
            //如果节点为多条则再取一层
            if(Constants.NumberStr.NUM_STR_ONE.equals(value)){
                elements = elements.get(0).elements();
            }
            List<Map<String, String>> lists = new ArrayList<>();
            for(Element element : elements){
                Map<String, String>  childDatas = new HashMap<>(16);
                List<Element> elements5 = element.elements();
                for(Element element1 : elements5){
                    String name = element1.getName();
                    String data = (String)element1.getData();
                    childDatas.put(name,data);
                }
                lists.add(childDatas);
                datas.put(key,lists);
            }
        }
        List<Map<String, String>> list = new ArrayList<>();
        Map<String, String> map = new HashMap<>(16);
        map.put("processType",processType);
        list.add(map);
        datas.put("processType",list);
        log.info("解析后的map集合：{}",datas);
        if(Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_A.equals(processType)){
            //添加ajxx信息
            List<Element> ajxx = elementAjxx.elements("JBXX").get(0).elements();
            List<Map<String, String>> ajxxlists = new ArrayList<>();
            Map<String, String>  childDatas = new HashMap<>(16);
            for(Element element : ajxx){
                String name = element.getName();
                String data = (String)element.getData();
                childDatas.put(name,data);
            }
            ajxxlists.add(childDatas);
            datas.put("AJXX",ajxxlists);
            //添加message信息
            List<Element> message = elementMessage.elements();
            List<Map<String, String>> messagelists = new ArrayList<>();
            Map<String, String>  childDatas1 = new HashMap<>(16);
            for(Element element : message){
                String name = element.getName();
                String data = (String)element.getData();
                childDatas1.put(name,data);
            }
            // 将文件名称存储在xml-Message信息中
            childDatas1.put("ZIPNAME",zipName);
            messagelists.add(childDatas1);
            datas.put("MESSAGE",messagelists);
        }else if(Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_C.equals(processType) || Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_D.equals(processType)){
            //添加message信息
            List<Element> message = elementMessage.elements();
            List<Map<String, String>> messagelists = new ArrayList<>();
            Map<String, String>  childDatas1 = new HashMap<>(16);
            for(Element element : message){
                String name = element.getName();
                String data = (String)element.getData();
                childDatas1.put(name,data);
            }
            childDatas1.put("ZIPNAME",zipName);
            messagelists.add(childDatas1);
            datas.put("MESSAGE",messagelists);
        }
        Result result = new Result();
        switch(processType){
            case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_A:
                //核查入库校验
                 result = interfaceDockingService.automaticVerification(datas);
                break;
            case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_C:
                //补录入库校验
                try {
                    result = interfaceXjService.saveInformationBl(datas);
                } catch(Exception e) {
                    throw new RuntimeException(e);
                }
                // result入库成功后保存附件文书信息
                if (null != result && null != result.getCode() && Constants.Number.NUM_ONE.equals(result.getCode())) {
                    HashMap<String, String> data = (HashMap<String, String>) result.getData();
                    String priNum = data.get("PRINUM");
                    String account = "testAccount";
                    // 出监评估后默认签收
                    verificationService.signFor(priNum, Constants.NumberStr.NUM_STR_TWO,null);
                    // 文书path
                    String localPath = FileUtil.getFilePathDetails().get("localpath") + "jy" + File.separator + zipName.split("\\.")[0] + File.separator + "ws";
                    File file = new File(localPath);
                    String[] fileNames = file.list();
                    for (int i = 0; i < Objects.requireNonNull(fileNames).length; i++) {
                        // 真实存储文件的目录
                        File file1 = new File(localPath + File.separator + fileNames[i]);
                        try {
                            saveAppendixFile(account,file1,IdGen.strid(),priNum,Constants.NumberStr.NUM_STR_TWO,IdGen.strid());
                        } catch (Exception e) {
                            log.error("湖南看守所对接获取文件异常:{}",e.getMessage());
                        }
                    }
                }
                break;
            case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_D:
                //衔接入库校验
                try {
                    List<Map<String, String>> list1 = datas.get("JBXX");
                    datas.put("FQXJ",list1);
                    datas.remove("JBXX");
                    result = interfaceXjService.insertXjxxInface(datas);
                } catch(Exception e) {
                    throw new RuntimeException(e);
                }
                break;
            default:
                break;
        }
        return result;
    }

    /**
     * 处理文件输出
     * @param hzParams map
     * @return Result
     */
    @Override
    public Result processOut(Map<String,String> hzParams) {
        //流程类型编码
        String process = Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO;
        //流程节点编码
        String processType = hzParams.get("PROCESS_NO");
        //发送单位类型
        String fromOrg = "5D";
        //接收单位类型
        String toOrg = "4B";
        //跨部门办案号
        String bah = hzParams.get("KBMBAH");
        //流程实例编号
        String shiliNo = hzParams.get("LCSLBH");
        //当前时间戳
        long l = System.currentTimeMillis();
        //任务号
        String taskNo = "SF" + l + RandomUtil.randomNumbers(2<<1);
        hzParams.put("RWH",taskNo);
        //MD5
        String md5Str;
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update((l+"").getBytes(StandardCharsets.UTF_8));
            // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
            String md5 = new BigInteger(1, md.digest()).toString(2<<3);
            //BigInteger会把0省略掉，需补全至32位
            md5Str = md5.length() == 2<<4 ? md5 : fillMd5("0" + md5);
            md5Str = md5Str.toUpperCase(Locale.ROOT);
        } catch (Exception e) {
            throw new RuntimeException("MD5加密错误:"+e.getMessage(),e);
        }
        String zipName = process + "_" + processType + "_" + fromOrg + "_" + toOrg + "_" + bah + "_" + shiliNo + "_" + taskNo + "_" + md5Str + ".zip";
        String xmlName = process + "_" + processType + "_" + fromOrg + "_" + toOrg + "_" + bah + "_" + shiliNo + "_" + taskNo + ".xml";
        //組裝xml文件
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement("ROOT");
        Element message = root.addElement("MESSAGE");
        Element ywxx = root.addElement("YWXX");
        root.addElement("WS");
        switch (processType) {
            // 预安置人员核查回执
            case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_B:
                // 跨部门人员编号
                ywxx.addElement("KBMRYBH").addText(hzParams.get("KBMRYBH"));
                // 罪犯编号
                ywxx.addElement("priNum").addText(hzParams.get("priNum"));
                // 姓名
                ywxx.addElement("NAME").addText(hzParams.get("NAME"));
                // 审查日期
                ywxx.addElement("SC_DATE").addText(hzParams.get("SC_DATE"));
                // 审查结果
                ywxx.addElement("SCJG_CODE").addText(hzParams.get("SCJG_CODE"));
                // 审查结果名称
                ywxx.addElement("SCJG_NAME").addText(hzParams.get("SCJG_NAME"));
                // 不予接收理由
                ywxx.addElement("BYJSLY").addText(hzParams.get("BYJSLY"));
                break;
            // 衔接成功与失败回执
            case Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO_F:
                // 衔接结果代码
                ywxx.addElement("XJJG_CODE").addText(hzParams.get("XJJG_CODE"));
                // 衔接结果名称
                ywxx.addElement("XJJG_NAME").addText(hzParams.get("XJJG_NAME"));
                // 受送达人
                ywxx.addElement("SSDR").addText(hzParams.get("SSDR"));
                // 接收时间
                ywxx.addElement("JS_TIME").addText(hzParams.get("JS_TIME"));
                // 备注
                ywxx.addElement("BZ").addText(hzParams.get("BZ"));
                break;
            default:
                break;
        }
        // message信息
        assembleElement(message,hzParams,Constants.NumberStr.NUM_STR_ONE,processType);
        OutputFormat format = OutputFormat.createPrettyPrint();
        // 指定XML编码
        format.setEncoding("UTF-8");
        String wsPath = FileUtil.getFilePathDetails().get("wsPath");
        String sacwPath = FileUtil.getFilePathDetails().get("sacwPath");
        String xmlpathTemp = FileUtil.getFilePathDetails().get("xmlpathTemp");
        String jzPath = FileUtil.getFilePathDetails().get("jzPath");
        String zipPath = FileUtil.getFilePathDetails().get("zipPath");
        XMLWriter writer = null;
        try {
            File file = new File(xmlpathTemp + taskNo);
            if(!file.exists()) {
                boolean mkdirs = file.mkdirs();
                if(mkdirs){
                    writer = new XMLWriter(new FileWriter(xmlpathTemp + taskNo + File.separator + xmlName),format);
                    writer.write(document);
                }
            }
        } catch(IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                Objects.requireNonNull(writer).close();
            } catch(IOException e) {
                log.error(e.getMessage());
            }
        }
        // copy文件到指定目录
        // xmlpathTemp + taskNo + File.separator + xmlName
        String localpath = FileUtil.getFilePathDetails().get("localpath");
        File file = new File(localpath + bah + File.separator + processType + taskNo);
        if(!file.exists()) {
            file.mkdirs();
        }
        FileUtil.copyFile(new File(xmlpathTemp + taskNo + File.separator + xmlName), new File(localpath + bah + File.separator + processType + taskNo + File.separator + xmlName));
        //空目录的临时目录
        if(new File(wsPath + taskNo + File.separator + "ws").mkdirs() && new File(sacwPath + taskNo + File.separator +"sacw").mkdirs() && new File(jzPath + taskNo + File.separator + "jz").mkdirs()){
            File zip = ZipUtil.zip(
                    cn.hutool.core.io.FileUtil.file(zipPath + taskNo + File.separator + zipName),
                    true,
                    cn.hutool.core.io.FileUtil.file(wsPath + taskNo + File.separator +"ws"),
                    cn.hutool.core.io.FileUtil.file(sacwPath + taskNo + File.separator + "sacw"),
                    cn.hutool.core.io.FileUtil.file(jzPath + taskNo + File.separator +"jz"),
                    cn.hutool.core.io.FileUtil.file(xmlpathTemp + taskNo + File.separator + xmlName));
            if(zip.exists()){
                new File(wsPath + taskNo + File.separator +"ws").delete();
                new File(sacwPath + taskNo + File.separator +"sacw").delete();
                new File(jzPath + taskNo + File.separator +"jz").delete();
                new File(xmlpathTemp + taskNo + File.separator + xmlName).delete();
            }
        }
        //将本地服务器zip包上传到ftp
        FTPClient ftp = FtpUtil.getFtpClient(pushFtpIp,pushFtpPort,pushFtpUsername,pushFtpPassword);
        //设置ftp连接超时时间5秒
        ftp.setConnectTimeout(tomeout);
        boolean uploadFileResult = FtpUtil.uploadFile(ftp,ftpUploadPath,zipPath + taskNo + File.separator + zipName);
        if(uploadFileResult){
            return Result.success();
        }
        return Result.failure(ReturnCode.FAILURE);
    }

    /**
     * 组装xml中message标签信息
     * @param message xml element标签
     * @param hzParams 解析xml后返回的信息
     * @param type 1:0702B,0702F的xml,2:0702A,0702C,0702D回执的xml
     * @param processType 流程节点编号
     */
    private void assembleElement(Element message,Map<String,String> hzParams,String type,String processType) {
        // 跨部门办案号
        message.addElement("KBMBAH").addText(hzParams.get("KBMBAH"));
        // 流程实例编号
        message.addElement("LCSLBH").addText(hzParams.get("LCSLBH"));
        // 任务号
        message.addElement("RWH").addText(hzParams.get("RWH"));
        // 案件名称
        message.addElement("AJMC").addText(hzParams.get("AJMC"));
        // 流程类型编码
        message.addElement("LCLX_CODE").addText(hzParams.get("LCLX_CODE"));
        // 流程类型名称
        message.addElement("LCLX_NAME").addText(hzParams.get("LCLX_NAME"));
        // 流程节点编号
        message.addElement("LCJDBH").addText(processType);
        // 接收单位类型
        message.addElement("JSDWLX").addText(hzParams.get("JSDWLX"));
        // 接收单位编码
        message.addElement("JSDW_CODE").addText(hzParams.get("JSDW_CODE"));
        // 接收单位名称
        message.addElement("JSDW_NAME").addText(hzParams.get("JSDW_NAME"));
        // 接收单位地址
        message.addElement("JSDWSZD").addText(hzParams.get("JSDWSZD"));
        // 接收单位行政区代码
        message.addElement("JSDWXZQ_CODE").addText(hzParams.get("JSDWXZQ_CODE"));
        // 接收单位行政区名称
        message.addElement("JSDWXZQ_NAME").addText(hzParams.get("JSDWXZQ_NAME"));
        // 发送单位类型
        message.addElement("FSDWLX").addText(hzParams.get("FSDWLX"));
        // 发送单位编码
        message.addElement("FSDW_CODE").addText(hzParams.get("FSDW_CODE"));
        // 发送单位名称
        message.addElement("FSDW_NAME").addText(hzParams.get("FSDW_NAME"));
        // 发送单位地址
        message.addElement("FSDWSZD").addText(hzParams.get("FSDWSZD"));
        // 发送单位行政区编码
        message.addElement("FSDWXZQ_CODE").addText(hzParams.get("FSDWXZQ_CODE"));
        // 发送单位行政区名称
        message.addElement("FSDWXZQ_NAME").addText(hzParams.get("FSDWXZQ_NAME"));
        // 发送时间
        message.addElement("FS_TIME").addText(hzParams.get("FS_TIME"));
        if(Constants.NumberStr.NUM_STR_TWO.equals(type)) {
            // 结果状态
            message.addElement("JGZT").addText(hzParams.get("JGZT"));
            // 状态描述
            message.addElement("ZTMS").addText(hzParams.get("MESSAGE"));
        }
        // 移送人
        message.addElement("YSR").addText(hzParams.get("YSR"));
        // 移送人电话
        message.addElement("YSRDH").addText(hzParams.get("YSRDH"));
        // 承办人
        message.addElement("CBR").addText(hzParams.get("CBR"));
        // 承办人电话
        message.addElement("CBRDH").addText(hzParams.get("CBRDH"));
    }

    /**
     * 处理返回值信息,封装xml文件
     * @param hzParams hzParams
     */
    @Override
    public void makeFile(Map<String, String> hzParams) {
        //流程类型编码
        String process = Constants.HnInterfaceDocking.ZERO_SEVEN_ZERO_TWO;
        //流程节点编码
        String processType = hzParams.get("PROCESS_NO");
        //发送单位类型
        String fromOrg = "5D";
        //接收单位类型
        String toOrg = "4B";
        //跨部门办案号
        String bah = hzParams.get("KBMBAH");
        //流程实例编号
        String shiliNo = hzParams.get("LCSLBH");
        //任务号
        String taskNo = hzParams.get("RWH");
        //时间戳
        long l = System.currentTimeMillis();
        //MD5
        String md5Str;
        try {
            // 生成一个MD5加密计算摘要
            MessageDigest md = MessageDigest.getInstance("MD5");
            // 计算md5函数
            md.update((l+"").getBytes(StandardCharsets.UTF_8));
            // digest()最后确定返回md5 hash值，返回值为8为字符串。因为md5 hash值是16位的hex值，实际上就是8位的字符
            // BigInteger函数则将8位的字符串转换成16位hex值，用字符串来表示；得到字符串形式的hash值
            String md5 = new BigInteger(1, md.digest()).toString(2<<3);
            //BigInteger会把0省略掉，需补全至32位
            md5Str = md5.length() == 2<<4 ? md5 : fillMd5("0" + md5);
            md5Str = md5Str.toUpperCase(Locale.ROOT);
        } catch (Exception e) {
            throw new RuntimeException("MD5加密错误:"+e.getMessage(),e);
        }
        // 文件命名
        String xmlName = process + "_" + processType + "_" + fromOrg + "_" + toOrg + "_" + bah + "_" + shiliNo + "_" + taskNo + "_"+md5Str + ".xml";
        String zipName = hzParams.get("ZIPNAME");
        //組裝xml文件
        Document document = DocumentHelper.createDocument();
        Element root = document.addElement("ROOT");
        Element message = root.addElement("MESSAGE");
        if(StringUtils.isNotBlank(processType)) {
            assembleElement(message,hzParams,Constants.NumberStr.NUM_STR_TWO,processType);
        }
        OutputFormat format = OutputFormat.createPrettyPrint();
        // 指定XML编码
        format.setEncoding("UTF-8");
        String localpath = FileUtil.getFilePathDetails().get("localpath");
        XMLWriter writer = null;
        // 本地目录只做备份,用于查询
        try {
            // 保存本地文件命名
            File file = new File(localpath + bah);
            if(!file.exists()) {
                // 创建文件
                file.mkdirs();
            }
            // 文件解压
            File unzip = ZipUtil.unzip(localpath + zipName, localpath + bah + File.separator + zipName.split("\\.")[0]);
            boolean exists = unzip.exists();
            if(exists){
                writer = new XMLWriter(new FileWriter(localpath + bah + File.separator + zipName.split("\\.")[0] + File.separator + xmlName),format);
                writer.write(document);
            }
        } catch(IOException e) {
            log.error(e.getMessage());
        } finally {
            try {
                Objects.requireNonNull(writer).close();
            } catch(IOException e) {
                log.error(e.getMessage());
            }
        }
        //空目录的临时目录
        File zip = ZipUtil.zip(localpath + bah + File.separator + zipName.split("\\.")[0]);
        if (zip.exists()) {
            //将本地服务器zip包上传到ftp
            FTPClient ftp = FtpUtil.getFtpClient(pushFtpIp,pushFtpPort,pushFtpUsername,pushFtpPassword);
            //设置ftp连接超时时间5秒
            ftp.setConnectTimeout(tomeout);
            FtpUtil.uploadFile(ftp,ftpUploadPath,localpath + bah + File.separator + zipName.split("\\.")[0] + File.separator + xmlName);
        }
    }

    /**
     * 描述
     * @param md5 md5
     * @return String
     */
    private static String fillMd5(String md5){
        //如果不够32位则回调自身补零，最后返回32位长度的签名
        return md5.length()==32?md5: fillMd5("0"+md5);
    }

    /**
     * 保存文件信息
     * @param file file
     * @param id id
     * @param priNum priNum
     * @param taskId taskId
     * @param singleId singleId
     */
    public void saveAppendixFile(String account,File file, String id, String priNum, String taskId, String singleId) {
        String fileName = file.getName();
        long dataId = IdGen.get().next();
            String tempFileName = account + File.separator + dataId + fileName.substring(fileName.lastIndexOf('.'));
            String filePath = FileUtil.getUploadFilePath() + tempFileName;
            File desFile = new File(filePath);
            if (!desFile.getParentFile().exists()) {
                try {
                    desFile.getParentFile().mkdirs();
                } catch (RuntimeException e) {
                    log.error(e.getMessage());
                }
            }
            FileInputStream inputStream;
            FileOutputStream outputStream;
            //上传文件到本地磁盘后异步上传文件到ftp
            try {
                inputStream = new FileInputStream(file);
                outputStream = new FileOutputStream(desFile);
                byte[] buffer= new byte[1024];
                int len;
                //将文件流信息读取文件缓存区，如果读取结果不为-1就代表文件没有读取完毕，反之已经读取完毕
                while ((len=inputStream.read(buffer))!=-1) {
                    outputStream.write(buffer, 0, len);
                    outputStream.flush();
                }
                inputStream.close();
                outputStream.close();
                // 获取ftp上传下载路径
                String datapath = FtpUtil.getFtpClientDetails().get("datapath");
                asyncUploadFileToFtp.uploadFileToFtp(datapath + account, filePath);
            } catch (IOException e) {
                log.error("发生异常:{}", e.getMessage());
            }
            UploadAppendixFile uploadAppendixFile = new UploadAppendixFile();
            uploadAppendixFile.setId(id);
            uploadAppendixFile.setSingleId(singleId);
            uploadAppendixFile.setTaskId(taskId);
            uploadAppendixFile.setPriNum(priNum);
            uploadAppendixFile.setFileName(fileName);
            uploadAppendixFile.setFileUrl(tempFileName);
            uploadAppendixFile.setAccount(account);
            uploadAppendixFile.setUserName(account);
            uploadAppendixFile.setUploadDate(new Date());
            uploadAppendixFileFeignService.saveAppendixFileData(uploadAppendixFile);
    }
}